//
// Created by pzw on 24-3-1.
//

#include "HashMap.h"

template<typename Key, typename Tp, typename Hash, typename Pred, typename Alloc>
HashMap<Key, Tp, Hash, Pred, Alloc>::HashMap(
        std::unordered_map<Key, Tp, Hash, Pred, Alloc> *m) : map(m) {
}

template<typename Key, typename Tp, typename Hash, typename Pred, typename Alloc>
HashMap<Key, Tp, Hash, Pred, Alloc>::~HashMap() {
    delete map;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::put(Key key, Tp value) {
    Tp oldValue = (*map)[key];
    (*map)[key] = value;
    return oldValue;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
void HashMap<Key, Tp, Hash, Pred, Alloc>::putAll(const std::unordered_map<Key, Tp> &m) {
    (*map).insert(m.begin(), m.end());
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::putIfAbsent(Key key, Tp value) {
    auto it = (*map).find(key);
    if (it == (*map).end()) {
        (*map)[key] = value;
        return Tp();
    } else {
        return it->second;
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
void HashMap<Key, Tp, Hash, Pred, Alloc>::replaceAll(
        std::function<Tp(const Key &, const Tp &)> function) {
    for (auto &pair: *map) {
        pair.second = function(pair.first, pair.second);
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::get(Key key) {
    auto it = (*map).find(key);
    if (it != (*map).end()) {
        return it->second;
    } else {
        return Tp();
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::containsKey(Key key) {
    return (*map).find(key) != (*map).end();
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::containsValue(Tp value) {
    for (const auto &pair: *map) {
        if (pair.second == value) {
            return true;
        }
    }
    return false;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::remove(Key key) {
    Tp value = Tp();
    auto it = (*map).find(key);
    if (it != (*map).end()) {
        value = it->second;
        (*map).erase(it);
    }
    return value;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::remove(Key key, Tp value) {
    auto it = (*map).find(key);
    if (it != (*map).end() && it->second == value) {
        (*map).erase(it);
        return true;
    }
    return false;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
void HashMap<Key, Tp, Hash, Pred, Alloc>::clear() {
    (*map).clear();
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
int HashMap<Key, Tp, Hash, Pred, Alloc>::size() {
    return (*map).size();
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::isEmpty() {
    return (*map).empty();
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
std::set<Key> HashMap<Key, Tp, Hash, Pred, Alloc>::keySet() {
    std::set<Key> keys;
    for (const auto &pair: *map) {
        keys.insert(pair.first);
    }
    return keys;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
std::vector<Tp> HashMap<Key, Tp, Hash, Pred, Alloc>::values() {
    std::vector<Tp> vals;
    for (const auto &pair: *map) {
        vals.push_back(pair.second);
    }
    return vals;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
std::set<std::pair<Key, Tp>> HashMap<Key, Tp, Hash, Pred, Alloc>::entrySet() {
    std::set<std::pair<Key, Tp>> entries;
    for (const auto &pair: *map) {
        entries.insert(pair);
    }
    return entries;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::getOrDefault(Key key, Tp defaultValue) {
    return (*map).find(key) != (*map).end() ? (*map)[key] : defaultValue;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::operator[](Key key) {
    return (*map).find(key) != (*map).end() ? (*map)[key] : nullptr;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
void HashMap<Key, Tp, Hash, Pred, Alloc>::forEach(
        std::function<void(const Key &, const Tp &)> action) {
    for (const auto &pair: *map) {
        action(pair.first, pair.second);
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::replace(Key key, Tp value) {
    if ((*map).find(key) != (*map).end()) {
        (*map)[key] = value;
        return value;
    } else {
        return Tp();
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::replace(Key key, Tp oldValue, Tp newValue) {
    auto it = (*map).find(key);
    if (it != (*map).end() && it->second == oldValue) {
        it->second = newValue;
        return true;
    }
    return false;
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::compute(
        Key key,
        std::function<Tp(const Key &, const Tp &)> remappingFunction
) {
    auto it = (*map).find(key);
    if (it != (*map).end()) {
        return it->second = remappingFunction(key, it->second);
    } else {
        return Tp();
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::computeIfAbsent(
        Key key,
        std::function<Tp(const Key &)> mappingFunction
) {
    auto it = (*map).find(key);
    if (it == (*map).end()) {
        return (*map)[key] = mappingFunction(key);
    } else {
        return it->second;
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
Tp HashMap<Key, Tp, Hash, Pred, Alloc>::computeIfPresent(
        Key key,
        std::function<Tp(const Key &, const Tp &)> remappingFunction
) {
    auto it = (*map).find(key);
    if (it != (*map).end()) {
        return it->second = remappingFunction(key, it->second);
    } else {
        return Tp();
    }
}

template<
        typename Key,
        typename Tp,
        typename Hash,
        typename Pred,
        typename Alloc
>
bool HashMap<Key, Tp, Hash, Pred, Alloc>::remove(Key key, Key value) {
    auto it = (*map).find(key);
    if (it != (*map).end() && it->second == value) {
        (*map).erase(it);
        return true;
    }
    return false;
}

int HashMap_main() {
    HashMap<int, std::string> *hashMap = new HashMap<int, std::string>(
            new std::unordered_map<int, std::string>()
    );
    if (hashMap == nullptr) {
        return -2;
    }
    for (int i = 0; i < 8; ++i) {
        hashMap->put(i, std::to_string(i));
    }

    if (hashMap->containsKey(2)) {
        hashMap->remove(2);
    }
    if (hashMap->containsKey(1)) {
        hashMap->remove(1);
    }
    if (hashMap->containsValue("0")) {
        const std::set<std::pair<int, std::string>> &entries = hashMap->entrySet();
        for (auto it = entries.begin(); it != entries.end(); it++) {
            if (it->second == "0") {
                hashMap->remove(it->first);
                std::cout << "HashMap_main: remove key == 0 if value == '0'" << std::endl;
            }
        }
    }

    if (hashMap->isEmpty()) {
        delete hashMap;
        return -1;
    }

    const std::set<std::pair<int, std::string>> &entries = hashMap->entrySet();
    int index = 0;
    for (auto it = entries.begin(); it != entries.end(); it++, index++) {
        std::cout << "HashMap_main: entries[" << index << "] = " << it->first << " -> " << it->second << std::endl;
    }

    const std::set<int> &keySet = hashMap->keySet();
    index = 0;
    for (auto it = keySet.begin(); it != keySet.end(); it++, index++) {
        std::cout << "HashMap_main: keySet[" << index << "] = " << (*it) << std::endl;
    }
    index = 0;
    for (int it: keySet) {
        index++;
        std::cout << "HashMap_main: keySet[" << index << "] = " << it << std::endl;
    }

    const std::vector<std::string> &values = hashMap->values();
    index = 0;
    for (auto it = values.begin(); it != values.end(); it++, index++) {
        std::cout << "HashMap_main: values[" << index << "] = " << (*it) << std::endl;
    }
    index = 0;
    for (std::string it: values) {
        index++;
        std::cout << "HashMap_main: values[" << index << "] = " << it << std::endl;
    }

    index = 0;
    int *pInt = &index;
    hashMap->forEach([pInt](const int &key, const std::string &value) -> void {
        (*pInt)++;
        std::cout << "HashMap_main: hashMap[" << (*pInt) << "] = " << key << "->" << value << std::endl;
    });

    hashMap->replace(7, "7", "8");
    const std::set<std::pair<int, std::string>> &entries2 = hashMap->entrySet();
    index = 0;
    for (auto it = entries2.begin(); it != entries2.end(); it++, index++) {
        std::cout << "HashMap_main: after replace, entries[" << index << "] = " << it->first << " -> " << it->second
                  << std::endl;
    }
    
    hashMap->clear();

    delete hashMap;
    return 0;
}
